/******************************************************************************
 * Copyright 2022 The Airos Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include <string>
#include <gflags/gflags.h>
#include <glog/logging.h>
#include "config/config.h"
#include "components/pc5_service/pc5_service.h"
#include "v2xpb-asn-message-frame.pb.h"

namespace air {
namespace net {

DEFINE_int32(pc5_asn_type, 1, "");
DEFINE_string(pc5_peer_ip, "172.26.20.135", "");
DEFINE_int32(pc5_peer_port, 10005, "");
DEFINE_int32(pc5_local_port, 10005, "");

int GetAidForPbstr(const std::string& data) {
  auto msg_frame = std::make_shared<::v2xpb::asn::MessageFrame>();
  if (!msg_frame || !msg_frame->ParsePartialFromString(data)) {
    return -1;
  }

  switch (msg_frame->payload_case()) {
    case ::v2xpb::asn::MessageFrame::PayloadCase::kBsmFrame: {
      return 111;
    }
    case ::v2xpb::asn::MessageFrame::PayloadCase::kMapFrame: {
      return 3618;
    }
    case ::v2xpb::asn::MessageFrame::PayloadCase::kSpatFrame: {
      return 3619;
    }
    case ::v2xpb::asn::MessageFrame::PayloadCase::kRsiFrame: {
      return 3620;
    }
    case ::v2xpb::asn::MessageFrame::PayloadCase::kRsmFrame: {
      return 3623;
    }
    default: {
      return -1;
    }
  }
}

void Pc5ServiceReceiver::OnInit() {
  sptr_module_.reset(&::air::net::ModuleUdp::getInstance());
  sptr_module_->Init(FLAGS_pc5_peer_ip, FLAGS_pc5_peer_port,
                     FLAGS_pc5_local_port);

  switch (FLAGS_pc5_asn_type) {
    case 1:
      asn_type_ = EnAsnType::CASE_53_2020;
      break;
    case 2:
      asn_type_ = EnAsnType::YDT_3709_2020;
      break;
    default:
      asn_type_ = EnAsnType::CASE_53_2020;
      break;
  }

  send_list_ = air::config::Config::GetInstance()
    ->GetSendList(GetWorkerName());
}

void Pc5ServiceReceiver::Run() {
  std::string data;
  if (sptr_module_) {
    sptr_module_->Recv(&data);
  }
  if (data.empty()) {
    LOG(ERROR) << "net service recv failed";
    return;
  }

  std::string asn_data;
  uint16_t aid = -1;
  dsmp_.Decode(data, &aid, &asn_data);
  LOG(INFO) << "recv msg aid:" << aid;

  // send to air api
  {
    auto msg = std::make_shared<air::link::Msg>();
    msg->SetData(asn_data);
    SendMsg("actor.AsnApi.AsnApi", msg);
  }

  // send to air secene
  std::string asnpb_data;
  auto res = message_frame_uper2pbstr_adapter(asn_data, &asnpb_data, asn_type_);
  if (res <= 0) {
    LOG(ERROR) << "convert asn to protobuf failed";
    return;
  }

  for (int i = 0; i < send_list_.size(); ++i) {
    DLOG(INFO) << "send to " << send_list_[i];
    auto msg = std::make_shared<air::link::Msg>();
    msg->SetData(asnpb_data);
    SendMsg(send_list_[i], msg);
  }
  DispatchMsg();
}

void Pc5ServiceReceiver::OnExit() {}

void Pc5ServiceSender::OnInit() {
  sptr_module_.reset(&::air::net::ModuleUdp::getInstance());

  switch (FLAGS_pc5_asn_type) {
    case 1:
      asn_type_ = EnAsnType::CASE_53_2020;
      break;
    case 2:
      asn_type_ = EnAsnType::YDT_3709_2020;
      break;
    default:
      asn_type_ = EnAsnType::CASE_53_2020;
      break;
  }
}

void Pc5ServiceSender::Run() {
  DispatchAndWaitMsg();
  while (RecvMsgListSize() > 0) {
    const auto& msg = GetRecvMsg();
    auto data = msg->GetData();
    std::string asn_data;
    auto res = message_frame_pbstr2uper_adapter(data, &asn_data, asn_type_);
    if (res <= 0) {
      LOG(ERROR) << "convert asn to protobuf failed";
      continue;
    }

    int aid = GetAidForPbstr(data);
    std::string str_out;
    dsmp_.Encode(asn_data, aid, &str_out);
    if (sptr_module_) {
      sptr_module_->Send(str_out, aid);
    }
  }
}

void Pc5ServiceSender::OnExit() {}

}  // namespace net
}  // namespace air
